/***********************************************************************
 *            Project: CoreCms
 *        ProjectName: 核心内容管理系统                                
 *                Web: https://www.corecms.net                      
 *             Author: 大灰灰                                          
 *              Email: jianweie@163.com                                
 *         CreateTime: 2021/1/31 21:45:10
 *        Description: 暂无
 ***********************************************************************/

using CoreCms.Net.Configuration;
using CoreCms.Net.IRepository;
using CoreCms.Net.IServices;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.ViewModels.UI;
using Newtonsoft.Json;
using System.Threading.Tasks;
using NLog;
using System;
using System.Globalization;
using CoreCms.Net.Model.Options;
using CoreCms.Net.Utility.Extensions;
using CoreCms.Net.Utility.Helper;
using Essensoft.Paylink.Alipay;
using Essensoft.Paylink.Alipay.Domain;
using Essensoft.Paylink.Alipay.Request;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using Aop.Api;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.WxaICPApplyICPFilingRequest.Types;
using Microsoft.IdentityModel.Tokens;


namespace CoreCms.Net.Services
{
    /// <summary>
    ///     支付宝支付 接口实现
    /// </summary>
    public class AliPayServices : BaseServices<CoreCmsSetting>, IAliPayServices
    {
        private readonly IAlipayClient _client;
        private readonly IServiceProvider _serviceProvider;
        private readonly IAlipayConfigServices _alipayConfigServices;
        private readonly AliPayOptions _options;


        public AliPayServices(IWeChatPayRepository dal, IServiceProvider serviceProvider, IAlipayClient client, IAlipayConfigServices alipayConfigServices, IOptions<AliPayOptions> options)
        {
            _serviceProvider = serviceProvider;
            _client = client;
            _alipayConfigServices = alipayConfigServices;
            _options = options.Value;
            BaseDal = dal;
        }

        #region 发起支付宝支付
        /// <summary>
        ///     发起支付宝支付
        /// </summary>
        /// <param name="entity">实体数据</param>
        /// <returns></returns>
        public async Task<WebApiCallBack> PubPay(CoreCmsBillPayments entity)
        {
            var jm = new WebApiCallBack();
            using var container = _serviceProvider.CreateScope();
            var billPaymentsServices = container.ServiceProvider.GetService<ICoreCmsBillPaymentsServices>();
            var _aliPayUserInfoServices = container.ServiceProvider.GetService<ICoreCmsAliPayUserInfoServices>();


            var tradeType = GlobalEnumVars.AliPayPayTradeType.ScanQRCodes.ToString();
            if (!string.IsNullOrEmpty(entity.parameters))
            {
                var jObj = (JObject)JsonConvert.DeserializeObject(entity.parameters);
                if (jObj != null && jObj.TryGetValue("trade_type", out var value))
                    tradeType = PayHelper.GetAliPayPayTradeType(value.ObjectToString());
            }

            var config = await _alipayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true && p.appType == tradeType);
            if (config == null)
            {
                jm.msg = "支付配置信息获取失败";
                return jm;
            }

            if (string.IsNullOrEmpty(config.notifyUrl))
            {
                jm.msg = "未获取到配置的通知地址";
                return jm;
            }

            var notifyUrl = config.notifyUrl.EndsWith("/") ? config.notifyUrl + "m-" + config.appId : config.notifyUrl + "/m-" + config.appId;

            //构建linkPay请求配置实体
            var payOptions = new AlipayOptions
            {
                AppId = config.appId,
                AlipayPublicKey = config.publicKey,
                AppPrivateKey = config.privateKey,
                ServerUrl = config.serverUrl,
                SignType = config.signType,
                AppPublicCert = config.appPublicCert,
                AlipayPublicCert = config.alipayPublicCert,
                AlipayRootCert = config.alipayRootCert
            };


            //扫码支付
            if (tradeType == GlobalEnumVars.AliPayPayTradeType.ScanQRCodes.ToString())
            {

                var model = new AlipayTradePrecreateModel
                {
                    OutTradeNo = entity.paymentId,
                    Subject = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle,
                    TotalAmount = entity.money.ToString(CultureInfo.InvariantCulture),
                    Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle
                };
                var req = new AlipayTradePrecreateRequest();
                req.SetBizModel(model);
                req.SetNotifyUrl(notifyUrl);
                //req.SetReturnUrl("https://pc.pro.demo.corecms.cn/order/payment/result");

                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝ScanQRCodes支付拼接APP入参", JsonConvert.SerializeObject(model));
                var response = await _client.ExecuteAsync(req, payOptions);
                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝ScanQRCodes支付返回数据", JsonConvert.SerializeObject(response));


                jm.data = response.Body;
                //response.OutTradeNo = entity.paymentId;
                jm.otherData = response;
                jm.status = !response.IsError;

            }
            //支付宝小程序支付
            else if (tradeType == GlobalEnumVars.AliPayPayTradeType.JSAPI.ToString())
            {
                var aliUserInfo = await _aliPayUserInfoServices.QueryByClauseAsync(p => p.userInfoId == entity.userId);
                if (aliUserInfo == null)
                {
                    jm.msg = "支付宝下单用户获取失败";
                    return jm;
                }

                Aop.Api.IAopClient alipayClient = new Aop.Api.DefaultAopClient(_options.AliPublicApi, _options.AppId, _options.AppSecret, "json", "1.0", "RSA2", _options.AliPublicKey, "utf-8", false);

                Aop.Api.Request.AlipayTradeCreateRequest request = new Aop.Api.Request.AlipayTradeCreateRequest();
                Aop.Api.Domain.AlipayTradeCreateModel model = new Aop.Api.Domain.AlipayTradeCreateModel();
                model.OutTradeNo = entity.paymentId;
                model.TotalAmount = entity.money.ToString(CultureInfo.InvariantCulture);
                model.Subject = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle;
                model.ProductCode = "JSAPI_PAY";
                model.OpAppId = _options.AppId;
                model.Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle;

                if (!string.IsNullOrEmpty(aliUserInfo.userId))
                {
                    model.BuyerId = aliUserInfo.userId;
                }
                if (!string.IsNullOrEmpty(aliUserInfo.openId))
                {
                    model.OpBuyerOpenId = aliUserInfo.openId;
                }
                request.SetBizModel(model);
                request.SetNotifyUrl(notifyUrl);

                Aop.Api.Response.AlipayTradeCreateResponse response = alipayClient.Execute(request);

                jm.data = new
                {
                    entity.paymentId,
                    response.TradeNo
                };
                jm.otherData = response;
                jm.status = !response.IsError;
                jm.msg = response.IsError ? response.SubMsg : "支付宝小程序支付参数构建成功。";

            }
            //PC网站支付
            else if (tradeType == GlobalEnumVars.AliPayPayTradeType.JSAPI_PC.ToString())
            {
                var model = new AlipayTradePagePayModel
                {
                    OutTradeNo = entity.paymentId,
                    Subject = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle,
                    ProductCode = "FAST_INSTANT_TRADE_PAY",
                    TotalAmount = entity.money.ToString(CultureInfo.InvariantCulture),
                    Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle
                };
                var req = new AlipayTradePagePayRequest();
                req.SetBizModel(model);
                req.SetNotifyUrl(notifyUrl);
                req.SetReturnUrl(config.jumpUrl);

                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝JSAPI_PC支付拼接APP入参", JsonConvert.SerializeObject(model));
                var response = await _client.PageExecuteAsync(req, payOptions);
                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝JSAPI_PC支付返回数据", JsonConvert.SerializeObject(response));


                jm.data = response.Body;
                response.TradeNo = entity.paymentId;
                jm.otherData = response;
                jm.status = !response.IsError;

            }
            //APP支付
            else if (tradeType == GlobalEnumVars.AliPayPayTradeType.APP.ToString())
            {
                var model = new AlipayTradeAppPayModel
                {
                    OutTradeNo = entity.paymentId,
                    Subject = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle,
                    //ProductCode = entity.paymentCode,
                    ProductCode = "QUICK_MSECURITY_PAY",
                    TotalAmount = entity.money.ToString(CultureInfo.InvariantCulture),
                    Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle
                };
                var req = new AlipayTradeAppPayRequest();
                req.SetBizModel(model);
                req.SetNotifyUrl(notifyUrl);
                //req.SetReturnUrl(config.jumpUrl);

                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝APP支付拼接APP入参", JsonConvert.SerializeObject(model));
                var response = await _client.SdkExecuteAsync(req, payOptions);
                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝APP支付返回数据", JsonConvert.SerializeObject(response));


                jm.data = response.Body;
                response.TradeNo = entity.paymentId;
                jm.otherData = response;
                jm.status = !response.IsError;

            }
            //H5支付
            else if (tradeType == GlobalEnumVars.AliPayPayTradeType.MWEB.ToString())
            {
                var model = new AlipayTradeWapPayModel
                {
                    OutTradeNo = entity.paymentId,
                    Subject = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle,
                    //ProductCode = entity.paymentCode,
                    ProductCode = "QUICK_WAP_PAY",
                    TotalAmount = entity.money.ToString(CultureInfo.InvariantCulture),
                    Body = entity.payTitle.Length > 40 ? entity.payTitle[..40] : entity.payTitle
                };
                var req = new AlipayTradeWapPayRequest();
                req.SetBizModel(model);
                req.SetNotifyUrl(notifyUrl);
                req.SetReturnUrl(config.jumpUrl);

                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝MWEB支付拼接APP入参", JsonConvert.SerializeObject(model));
                var response = await _client.PageExecuteAsync(req, payOptions);
                Loging.NLogUtil.WriteAll(LogLevel.Trace, Loging.LogType.Order, "支付宝MWEB支付返回数据", JsonConvert.SerializeObject(response));


                jm.data = response.Body;
                response.TradeNo = entity.paymentId;
                jm.otherData = response;
                jm.status = !response.IsError;

            }


            return jm;
        }

        #endregion

        #region 用户退款
        /// <summary>
        ///     用户退款
        /// </summary>
        /// <param name="refundInfo">退款单数据</param>
        /// <param name="paymentInfo">支付单数据</param>
        /// <returns></returns>
        public async Task<WebApiCallBack> Refund(CoreCmsBillRefund refundInfo, CoreCmsBillPayments paymentInfo)
        {
            var jm = new WebApiCallBack();

            var tradeType = GlobalEnumVars.WeiChatPayTradeType.JSAPI.ToString();
            if (!string.IsNullOrEmpty(paymentInfo.parameters))
            {
                var jObj = (JObject)JsonConvert.DeserializeObject(paymentInfo.parameters);
                if (jObj != null && jObj.TryGetValue("trade_type", out var value))
                    tradeType = PayHelper.GetAliPayPayTradeType(value.ObjectToString());
            }

            var config = await _alipayConfigServices.QueryByClauseAsync(p => p.isDefault == true && p.isEnable == true && p.appType == tradeType);
            if (config == null)
            {
                jm.msg = "支付配置信息获取失败";
                return jm;
            }

            if (string.IsNullOrEmpty(config.refundUrl))
            {
                jm.msg = "未获取到配置的退款通知地址";
                return jm;
            }

            //构建linkPay请求配置实体
            var payOptions = new AlipayOptions
            {
                AppId = config.appId,
                AlipayPublicKey = config.publicKey,
                AppPrivateKey = config.privateKey,
                ServerUrl = config.serverUrl,
                SignType = config.signType,
                AppPublicCert = config.appPublicCert,
                AlipayPublicCert = config.alipayPublicCert,
                AlipayRootCert = config.alipayRootCert
            };


            var model = new AlipayTradeRefundModel
            {
                OutTradeNo = paymentInfo.paymentId,
                TradeNo = paymentInfo.tradeNo,
                RefundAmount = refundInfo.money.ToString(CultureInfo.InvariantCulture),
                OutRequestNo = refundInfo.refundId,
                RefundReason = "用户申请退款",
            };


            var req = new AlipayTradeRefundRequest();
            req.SetBizModel(model);

            var response = await _client.ExecuteAsync(req, payOptions);

            jm.msg = !response.IsError ? "退款成功" : "退款失败";
            jm.status = !response.IsError;
            jm.data = response;


            return jm;
        }

        #endregion

        #region 换取授权访问令牌(alipay.system.oauth.token)
        /// <summary>
        /// 根据票据返回app_auth_token相关信息
        /// </summary>
        /// <param name="code">票据</param>
        /// <returns></returns>
        public Aop.Api.Response.AlipaySystemOauthTokenResponse GetAliPayAppAuthTokenBYCode(string code)
        {
            Aop.Api.IAopClient alipayClient = new Aop.Api.DefaultAopClient(_options.AliPublicApi, _options.AppId, _options.AppSecret, "json", "1.0", "RSA2", _options.AliPublicKey, "utf-8", false);

            Aop.Api.Request.AlipaySystemOauthTokenRequest request = new Aop.Api.Request.AlipaySystemOauthTokenRequest();

            request.GrantType = "authorization_code";
            request.Code = code;
            request.RefreshToken = _options.AliAccessTokenRefresh;

            Aop.Api.Response.AlipaySystemOauthTokenResponse response = alipayClient.Execute(request);

            return response;
        }
        #endregion


        #region 支付宝会员授权信息查询接口(alipay.user.info.share)
        /// <summary>
        /// 根据Token返回支付宝用户基本信息
        /// </summary>
        /// <param name="token">Token</param>
        public Aop.Api.Response.AlipayUserInfoShareResponse GetAliPayUserInfoByToken(string token)
        {
            Aop.Api.IAopClient alipayClient = new Aop.Api.DefaultAopClient(_options.AliPublicApi, _options.AppId, _options.AppSecret, "json", "1.0", "RSA2", _options.AliPublicKey, "utf-8", false);

            Aop.Api.Request.AlipayUserInfoShareRequest request = new Aop.Api.Request.AlipayUserInfoShareRequest();
            Aop.Api.Response.AlipayUserInfoShareResponse response = alipayClient.Execute(request, token);

            return response;
        }

        #endregion


    }
}